#include "util_2dquad.h"

//-----------------ACCESSORS/MUTATORS--------------------------------------------------------------------

/*******************************************
* Get the Number of Indices in this Object.
*******************************************/
int LJMUUtilQuad::getIndexCount()
{
	return _index_count;
}

/*******************************************
* Get a Reference to the Texture of the
* Sprite.
*******************************************/
ID3D10ShaderResourceView* LJMUUtilQuad::getTexture()
{
	return _obj_dx_texture->getTexture();
}

//-----------------PUBLIC METHODS------------------------------------------------------------------------

/*******************************************
* Initialise our Direct X Data Buffers.
*******************************************/
bool LJMUUtilQuad::initialise(ID3D10Device* pdevice, int pscreenwidth, int pscreenheight,std::wstring ptexfilename, int pbmpwidth, int pbmpheight)
{
	bool tinitsuccess;
	
	// Store the screen size.
	_screen_width = pscreenwidth;
	_screen_height = pscreenheight;

	// Store the size in pixels that this bitmap should be rendered at.
	_bitmap_width = pbmpwidth;
	_bitmap_height = pbmpheight;

	// Initialize the previous rendering position to negative one.
	_prev_pos_x = -1;
	_prev_pos_y = -1;

	// Initialize the vertex and index buffer that hold the geometry for the triangle.
	tinitsuccess = internInitBuffers(pdevice);
	
	//Return before Loading the Texture, since we have no buffer to put it in anyway......
	if(!tinitsuccess)
		return false;

	// Load the texture for this model.
	tinitsuccess = internLoadTextureInMem(pdevice, ptexfilename);
	
	//Return Whether the Load operation was successful
	return tinitsuccess;
}

/*******************************************
* Render our Sprite Quad by updating the 
* buffers and then outputing them through
* the GPU.
*******************************************/
bool LJMUUtilQuad::render(ID3D10Device* pdevice, int pposx, int pposy)
{
	bool tbuffupdatesuccess;
	
	// Re-build the dynamic vertex buffer for rendering to possibly a different location on the screen.
	tbuffupdatesuccess = internUpdateBuffers(pposx, pposy);
	if(!tbuffupdatesuccess)
		return false;

	// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
	internRenderBuffers(pdevice);
	return true;
}

/*******************************************
* Clean up the Quad by releasing our memory
* from the texture and the buffer objects.
*******************************************/
void LJMUUtilQuad::cleanup()
{
	// Release the model texture.
	internReleaseTextureFromMem();

	// Release the vertex and index buffers.
	internCleanupBuffers();

	return;
}

//-----------------PRIVATE METHODS------------------------------------------------------------------------

/*******************************************
*  Initialise our Buffer Objects.
*******************************************/
bool LJMUUtilQuad::internInitBuffers(ID3D10Device* pdevice)
{
	QuadVertexType* tvertices;
	unsigned long*	tindices;

	D3D10_BUFFER_DESC		tobj_dx_desc_vb;
	D3D10_SUBRESOURCE_DATA	tobj_dx_data_vb;
	D3D10_BUFFER_DESC		tobj_dx_desc_ib;
	D3D10_SUBRESOURCE_DATA	tobj_dx_data_ib;
	HRESULT					tinitsuccess;
	

	// Set the number of vertices in the vertex array.
	_vertex_count = 6;

	// Set the number of indices in the index array.
	_index_count = _vertex_count;

	// Create the vertex array.
	tvertices = new QuadVertexType[_vertex_count];
	if(!tvertices)
		return false;

	// Create the index array.
	tindices = new unsigned long[_index_count];
	if(!tindices)
		return false;

	// Initialize vertex array to zeros at first.
	memset(tvertices, 0, (sizeof(QuadVertexType) * _vertex_count));

	// Load the index array with data.
	int i;
	for(i=0; i<_index_count; i++)
	{
		tindices[i] = i;
	}

	// Set up the description of the dynamic vertex buffer.
	tobj_dx_desc_vb.Usage			= D3D10_USAGE_DYNAMIC;
    tobj_dx_desc_vb.ByteWidth		= sizeof(QuadVertexType) * _vertex_count;
    tobj_dx_desc_vb.BindFlags		= D3D10_BIND_VERTEX_BUFFER;
    tobj_dx_desc_vb.CPUAccessFlags	= D3D10_CPU_ACCESS_WRITE;
    tobj_dx_desc_vb.MiscFlags		= 0;

	// Give the subresource structure a pointer to the vertex data.
    tobj_dx_data_vb.pSysMem = tvertices;

	// Now finally create the vertex buffer.
    tinitsuccess = pdevice->CreateBuffer(&tobj_dx_desc_vb, &tobj_dx_data_vb, &_obj_buff_vertex);
	if(FAILED(tinitsuccess))
	{
		return false;
	}

	// Set up the description of the index buffer.
    tobj_dx_desc_ib.Usage = D3D10_USAGE_DEFAULT;
    tobj_dx_desc_ib.ByteWidth = sizeof(unsigned long) * _index_count;
    tobj_dx_desc_ib.BindFlags = D3D10_BIND_INDEX_BUFFER;
    tobj_dx_desc_ib.CPUAccessFlags = 0;
    tobj_dx_desc_ib.MiscFlags = 0;

	// Give the subresource structure a pointer to the index data.
    tobj_dx_data_ib.pSysMem = tindices;

	// Create the index buffer.
	tinitsuccess = pdevice->CreateBuffer(&tobj_dx_desc_ib, &tobj_dx_data_ib, &_obj_buff_index);
	if(FAILED(tinitsuccess))
		return false;

	// Release the arrays now that the vertex and index buffers have been created and loaded.
	delete [] tvertices;
	tvertices = 0;

	delete [] tindices;
	tindices = 0;

	return true;
}

/*******************************************
* Clean up the data used in our sprite quad.
*******************************************/
void LJMUUtilQuad::internCleanupBuffers()
{
	// Release the index buffer.
	if(_obj_buff_index)
	{
		_obj_buff_index->Release();
		_obj_buff_index = 0;
	}

	// Release the vertex buffer.
	if(_obj_buff_vertex)
	{
		_obj_buff_vertex->Release();
		_obj_buff_vertex = 0;
	}
	return;
}

/*******************************************
* Update the State of the buffers based on 
* our current position.
*******************************************/
bool LJMUUtilQuad::internUpdateBuffers(int pposx, int pposy)
{
	float		tpos_left;
	float		tpos_right;
	float		tpos_top;
	float		tpos_bottom;
	
	QuadVertexType* tvertices;
	void*			tptrvertices;
	HRESULT			tupdatesuccess;


	// If the position we are rendering this bitmap to has not changed then don't update the vertex buffer since it
	// currently has the correct parameters.
	if((pposx == _prev_pos_x) && (pposy == _prev_pos_y))
		return true;
	
	// If it has changed then update the position it is being rendered to.
	_prev_pos_x = pposx;
	_prev_pos_y = pposy;

	// Calculate the screen coordinates of the left side of the bitmap.
	tpos_left = (float)((_screen_width / 2) * -1) + (float)pposx;
	// Calculate the screen coordinates of the right side of the bitmap.
	tpos_right = tpos_left + (float)_bitmap_width;
	// Calculate the screen coordinates of the top of the bitmap.
	tpos_top = (float)(_screen_height / 2) - (float)pposy;
	// Calculate the screen coordinates of the bottom of the bitmap.
	tpos_bottom = tpos_top - (float)_bitmap_height;

	// Create the vertex array.
	tvertices = new QuadVertexType[_vertex_count];
	if(!tvertices)
		return false;

	// Load the vertex array with data.
	// First triangle.
	tvertices[0].screenpos	= D3DXVECTOR3(tpos_left, tpos_top, 0.0f);  // Top left.
	tvertices[0].textureuv	= D3DXVECTOR2(0.0f, 0.0f);

	tvertices[1].screenpos	= D3DXVECTOR3(tpos_right, tpos_bottom, 0.0f);  // Bottom right.
	tvertices[1].textureuv	= D3DXVECTOR2(1.0f, 1.0f);

	tvertices[2].screenpos	= D3DXVECTOR3(tpos_left, tpos_bottom, 0.0f);  // Bottom left.
	tvertices[2].textureuv	= D3DXVECTOR2(0.0f, 1.0f);

	// Second triangle.
	tvertices[3].screenpos	= D3DXVECTOR3(tpos_left, tpos_top, 0.0f);  // Top left.
	tvertices[3].textureuv	= D3DXVECTOR2(0.0f, 0.0f);

	tvertices[4].screenpos	= D3DXVECTOR3(tpos_right, tpos_top, 0.0f);  // Top right.
	tvertices[4].textureuv	= D3DXVECTOR2(1.0f, 0.0f);

	tvertices[5].screenpos	= D3DXVECTOR3(tpos_right, tpos_bottom, 0.0f);  // Bottom right.
	tvertices[5].textureuv	= D3DXVECTOR2(1.0f, 1.0f);

	// Initialize the vertex buffer pointer to null first.
	tptrvertices = 0;

	// Lock the vertex buffer.
	tupdatesuccess = _obj_buff_vertex->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&tptrvertices);
	if(FAILED(tupdatesuccess))
		return false;

	// Copy the data into the vertex buffer.
	memcpy(tptrvertices, (void*)tvertices, (sizeof(QuadVertexType) * _vertex_count));

	// Unlock the vertex buffer.
	_obj_buff_vertex->Unmap();

	// Release the vertex array as it is no longer needed (its now in the buffer!!)
	delete [] tvertices;
	tvertices = 0;
	return true;
}

/*******************************************
* Render our Buffers by using the Input
* Assembler Stage to pass through our
* data to the GPU.
*******************************************/
void LJMUUtilQuad::internRenderBuffers(ID3D10Device* pdevice)
{
	unsigned int tstride;
	unsigned int toffset;
	
	// Set vertex buffer stride and offset.
    tstride = sizeof(QuadVertexType); 
	toffset = 0;
    
	// Set the vertex buffer to active in the input assembler so it can be rendered.
	pdevice->IASetVertexBuffers(0, 1, &_obj_buff_vertex, &tstride, &toffset);

    // Set the index buffer to active in the input assembler so it can be rendered.
    pdevice->IASetIndexBuffer(_obj_buff_index, DXGI_FORMAT_R32_UINT, 0);

    // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
    pdevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	return;
}

/*******************************************
* Load a Texture Object with the given
* file location into our GPU Memory.
*******************************************/
bool LJMUUtilQuad::internLoadTextureInMem(ID3D10Device* pdevice,std::wstring pfilename)
{
	bool tloadsuccess;
	
	// Create the texture object.
	_obj_dx_texture = new LJMUUtilTexture;
	if(!_obj_dx_texture)
		return false;

	// Initialize the texture object.
	tloadsuccess = _obj_dx_texture->initialise(pdevice, pfilename);
	return tloadsuccess;
}

/*******************************************
* Release the Texture Object from our
* GPU Memory.
*******************************************/
void LJMUUtilQuad::internReleaseTextureFromMem()
{
	// Release the texture object.
	if(_obj_dx_texture)
	{
		_obj_dx_texture->cleanup();
		delete _obj_dx_texture;
		_obj_dx_texture = 0;
	}
	return;
}